import * as ts from "typescript";
import * as fs from 'fs';

/**
 * ast节点类，属性包括父节点，子节点列表，种类，文本内容，开始位置
 */
export class NodeA{
    children:NodeA[];
    kind:string;
    text:string;
    start:number;
    parent:NodeA|null;
    index:number;
    private static previousIndex: number = 0;
    constructor(node:ts.Node|undefined,children:NodeA[],text:string,start:number,parent:NodeA|null){
        this.children=children;
        this.text=text;
        this.start=start;
        this.parent=parent;
        this.index = ++NodeA.previousIndex;
        if(node==undefined){
            this.kind="undefined";
        }
        else{
            this.kind=ts.SyntaxKind[node.kind];
        }
    }
    getChildren(){
        return this.children;
    }
}
export class ASTEdge{
    u:number;//起始节点下标
    v:number;//终止节点下标
    uLine:number;//起始节点行号
    vLine:number;//终止节点行号
    type:number;//边类型,0为cfg,1为cdg

    constructor(u:number,v:number,uLine:number,vLine:number,type:number){
        this.u = u;
        this.v = v;
        this.uLine = uLine;
        this.vLine = vLine;
        this.type = type;
    }
}
/**
 * ast类，目前的构造方式是传ts代码，之后可以考虑传文件路径等
 */
export class ASTree{
    text:string;
    root:NodeA=new NodeA(undefined,[],"undefined",0,null);
    sourceFile:ts.SourceFile;
    ed:ASTEdge[];//边列表
    stmArray:NodeA[];//节点列表
    constructor(text:string){
        this.text=text;
        this.sourceFile = ts.createSourceFile(
            "example.ts",
            this.text,
            ts.ScriptTarget.Latest
        );
        this.ed = [];
        this.stmArray=[];
        this.buildTree();
        
    }
    addEdge(u:number,v:number,type:number){
        let lLine=0,rLine=0;
        if(this.stmArray[u]!=null)lLine = this.stmArray[u].start;
        if(this.stmArray[v]!=null)rLine = this.stmArray[v].start;
        let e:ASTEdge = new ASTEdge(u, v, lLine, rLine, type);
        this.ed.push(e);
    }
    buildTreeGraph(nodea:NodeA){
        let children=nodea?.getChildren();
        if (children==null) {
            return;
        }
        for(let child of children){
           this.addEdge(nodea.index,child.index,2);
        } for(let child of children){
        this.buildTreeGraph(child)}
    }
    /**
     * 复制typescript的ast，因为typescript的ast的节点不能直接操作，因此通过之前自己建立的节点类进行复制
     * @param nodea 复制到nodea
     * @param node 要复制的节点node
     * @returns 
     */
    copyTree(nodea:NodeA,node:ts.Node|undefined) {
        let children=node?.getChildren(this.sourceFile);
        if (children==null) {
            return;
        }
        let cas:NodeA[]=[];
        for(let child of children){
            let ca=new NodeA(child,[],child.getText(this.sourceFile),child.getStart(this.sourceFile),nodea)
            this.stmArray.push(ca)
            this.copyTree(ca,child)
            cas.push(ca)
        }
        nodea.children=cas
    }

    // 建树
    buildTree(){
        const rootN=this.sourceFile.getChildren(this.sourceFile)[0]
        if (rootN==null)
            process.exit(0);
        const rootA=new NodeA(rootN,[],rootN.getText(this.sourceFile),rootN.getStart(this.sourceFile),null)
        this.root=rootA
        this.stmArray.push(this.root)
        this.copyTree(rootA,rootN)
    }



    singlePrintAST(node:NodeA,i:number){
        console.log(' '.repeat(i)+node.kind)
        if (node.children==null)
            return;
        for(let c of node.children){
            this.singlePrintAST(c,i+1)
        }
    }

    printAST(){
        if(this.root==null){
            console.log("no root")
        }
        this.singlePrintAST(this.root,0)
    }
}

let fileContent = fs.readFileSync('t.ts', 'utf8');
let ast:ASTree=new ASTree(fileContent);
// ast.printAST()